import numpy as np
import os
import sys
import json


class predictInterest:
    def __init__(self):
        self.ratingsData_save_path = (
            os.path.abspath(".") + "/python-api/resource/ratings.csv"
        )
        self.simData_save_path = (
            os.path.abspath(".") + "/python-api/resource/user_similarity.csv"
        )

    def cosine_similarity(self, u, v):
        if np.all(u == 0) | np.all(v == 0):
            return 0
        # 计算余弦相似度
        return np.dot(u, v) / (np.linalg.norm(u) * np.linalg.norm(v))

    # 根据新的数据，计算相似矩阵并保存
    def reOperateSimilarity(self, ratings):
        # 用户数量和物品数量
        num_users, num_items = ratings.shape

        # 初始化用户相似度矩阵
        user_similarity = np.zeros((num_users, num_users))

        # 计算用户之间的相似度
        for i in range(num_users):
            for j in range(i, num_users):
                if i == j:
                    # 用户与自身的相似度为1
                    user_similarity[i][j] = 1
                else:
                    user_similarity[i][j] = self.cosine_similarity(
                        ratings[i], ratings[j]
                    )
                    user_similarity[j][i] = user_similarity[i][j]
        # 保存到指定路径中
        np.savetxt(self.ratingsData_save_path, ratings)
        np.savetxt(self.simData_save_path, user_similarity)

    # 从文本文件中加载数据
    def loadData(self):
        self.ratings = np.loadtxt(self.ratingsData_save_path)
        self.user_similarity = np.loadtxt(self.simData_save_path)

    def prediction(self, userID):
        # 以行为单位计算平均值
        mean_user_rating = self.ratings.mean(axis=1)
        # 计算评分与平均分的误差，因为要考虑评分为0的食品
        ratings_diff = self.ratings - mean_user_rating[:, np.newaxis]
        # 将用户相似度矩阵的第userID行与评分差异矩阵进行点积运算，得到用户对物品的兴趣程度的加权和
        # 将加权和除以用户相似度矩阵中该行的绝对值之和，得到预测评分。这是为了将加权和进行归一化，确保预测评分的范围在原始评分值之间。
        pred = mean_user_rating[userID] + self.user_similarity[userID].dot(
            ratings_diff
        ) / np.array([np.abs(self.user_similarity[userID]).sum(axis=0)])
        return pred


if __name__ == "__main__":
    refreshflag = sys.argv[1]
    predictionObject = predictInterest()
    # 如果要进行的操作是更新数据，则调用更新函数
    if refreshflag == "1":
        ratingsDataStr = sys.argv[2]
        ratingData = json.loads(ratingsDataStr)
        # loads后是list，list没有shape信息，所以转为array
        ratingData = np.array(ratingData)
        predictionObject.reOperateSimilarity(ratingData)
    else:
        userID = sys.argv[2]
        # 加载数据
        predictionObject.loadData()
        # 获取用户对每个物品的兴趣度评分
        predictionScore = predictionObject.prediction(int(userID))
        for i in range(predictionScore.size):
            print(predictionScore[i])

        # SELECT * FROM orders LIMIT 0,1;
        # 根据这个来获取第1条数据，0指从第一行开始，1表示限制一行数据
